<?php if (! defined('BASEPATH')) exit('No direct script access allowed');

/**
 * Ucenter通知类库
 *
 * UCenter的API接口文件必须存放于应用程序根目录的“api/”目录中
 * 且文件名可以自定义,默认为uc.php,即“api/uc.php”
 *
 * 文件结构可分为4部分(包含14个接口项目)
 * 第1部分为常量定义
 * 第2部分为通知方式判断以及初始化输入参数
 * 第3部分为接口类代码段(逻辑部分需要根据不同的应用自行编写处理)
 * 第4部分为函数定义
 *
 * 常量定义部分定义了接口所用到的14个接口项目的开关设置
 * 1为开启
 * 0为关闭
 * 如果开启,则代码段运行后返回 API_RETURN_SUCCEED
 * 否则返回 API_RETURN_FORBIDDEN
 * 接口运行失败则返回 API_RETURN_FAILED
 *
 * 功能使用:
 * http://faq.comsenz.com/library/UCenteruse/usebasicset/usebasicset_index.htm
 * 数据字典:
 * http://faq.comsenz.com/library/database/uc/uc_index.htm
 * 接口开发:
 * http://faq.comsenz.com/library/UCenter/introduction/introduction_brief.htm
 */

class Uc_note {

    /************************ 第1部分 常量定义 ************************/

    //版本
    const UC_CLIENT_RELEASE = '20110501'; //UCenter 发布时间
    const UC_CLIENT_VERSION = '1.6.0'; //UCenter 版本标识

    //14个接口项目
    const API_DELETEUSER = 1; //用户删除 API 接口开关
    const API_RENAMEUSER = 1; //用户改名 API 接口开关
    const API_GETTAG = 1; //获取标签 API 接口开关
    const API_SYNLOGIN = 1; //同步登录 API 接口开关
    const API_SYNLOGOUT = 1; //同步登出 API 接口开关
    const API_UPDATEPW = 1; //更改用户密码 开关
    const API_UPDATEBADWORDS = 1; //更新关键字列表 开关
    const API_UPDATEHOSTS = 1; //更新域名解析缓存 开关
    const API_UPDATEAPPS = 1; //更新应用列表 开关
    const API_UPDATECLIENT = 1; //更新客户端缓存 开关
    const API_UPDATECREDIT = 0; //更新用户积分 开关
    const API_GETCREDITSETTINGS = 0; //向 UCenter 提供积分设置 开关
    const API_GETCREDIT = 0; //获取用户的某项积分 开关
    const API_UPDATECREDITSETTINGS = 0; //更新应用积分设置 开关

    //返回值
    const API_RETURN_SUCCEED = 1; //接口项目如果开启，则代码段运行后返回该值
    const API_RETURN_FAILED = -1; //接口项目运行失败则返回该值
    const API_RETURN_FORBIDDEN = -2; //接口项目如果关闭，则代码段运行后返回该值

    public $get = array(); //获取GET内容
    public $post = array(); //获取POST内容

    /**
     * 初始化
     */
    public function __construct() {
        //载入配置
        $filename = APPPATH . 'config/ucenter.php';
        if (file_exists($filename)) require $filename;
        if (! defined('UC_API')) die('Access denied');

        $this->get = $this->get(); //获取GET内容
        $this->post = $this->post(); //获取POST内容
        log_message('debug', "Uc_note Class Initialized");
    }

    /**
     * 使得CI超级全局对象可以直接使用
     *
     * @param	$key
     * @return	mixed
     */
    public function __get($key) {
        $CI = &get_instance();
        return $CI->$key;
    }

    /************************ 第2部分 通知方式判断 ************************/

    /**
     * 执行并返回结果
     */
    public function run() {
        $timestamp = isset($_SERVER['REQUEST_TIME']) ? $_SERVER['REQUEST_TIME'] : time();
        if (empty($this->get)) die('非法请求');
        if ($timestamp - $this->get['time'] > 3600) die('授权已过期');

        if ($this->valid_action()) {
            echo $this->{$this->get['action']}();
        } else {
            echo self::API_RETURN_FAILED;
        }
    }

    /**
     * 验证方法
     */
    public function valid_action() {
        $actions = array(
            'test',
            'deleteuser',
            'renameuser',
            'gettag',
            'synlogin',
            'synlogout',
            'updatepw',
            'updatebadwords',
            'updatehosts',
            'updateapps',
            'updateclient',
            'updatecredit',
            'getcreditsettings',
            'getcredit',
            'updatecreditsettings');
        if (in_array($this->get['action'], $actions)) return true;
        return false;
    }

    /**
     * 获取GET内容
     */
    public function get() {
        $get = array();
        $code = $this->input->get('code', true);
        parse_str(self::authcode($code, 'DECODE', UC_KEY), $get);
        return $get;
    }

    /**
     * 获取POST内容
     */
    public function post() {
        $post = array();
        $post = self::unserialize(file_get_contents('php://input'));
        return $post;
    }

    /************************ 第3部分 接口类代码段 ************************/

    /**
     * 接口测试
     * 此接口供仅测试连接
     */
    private function test() {
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 用户删除
     * 当 UCenter 删除一个用户时,会发起 deleteuser 的接口请求,通知所有应用程序删除相应的用户
     *
     * @return int
     */
    private function deleteuser() {
        if (! self::API_DELETEUSER) return self::API_RETURN_FORBIDDEN;

        $uids = $this->get['ids'];
        //TODO: 删除用户操作
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 用户改名
     * 当 UCenter 更改一个用户的用户名时,会发起 renameuser 的接口请求,通知所有应用程序改名
     *
     * @return int
     */
    private function renameuser() {
        if (! self::API_RENAMEUSER) return self::API_RETURN_FORBIDDEN;

        $uid = $this->get['uid'];
        $usernameold = $this->get['oldusername'];
        $usernamenew = $this->get['newusername'];
        //TODO: 用户改名操作
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 获取标签
     * 如果应用程序存在标签功能,可以通过此接口把应用程序的标签数据传递给 UCenter
     *
     * @return int
     */
    private function gettag() {
        if (! self::API_GETTAG) return self::API_RETURN_FORBIDDEN;

        $name = $this->get['id'];
        $return = array();
        return self::serialize($return, 1);
    }

    /**
     * 同步登录
     * 如果应用程序需要和其他应用程序进行同步登录,此部分代码负责标记指定用户的登录状态
     *
     * @return int
     */
    private function synlogin() {
        if (! self::API_SYNLOGIN) return self::API_RETURN_FORBIDDEN;

        header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
        $uid = $this->get['uid'];
        //同步登录的代码在这里处理
        include FCPATH . 'uc_client/client.php';
        if ($uc_user = uc_get_user($uid, 1)) {
            $this->load->library('session');
            $this->session->set_userdata('user', array('uid' => $uid, 'username' => $uc_user[1]));
        }

        return self::API_RETURN_SUCCEED;
    }

    /**
     * 同步登出
     * 如果应用程序需要和其他应用程序进行同步退出登录,此部分代码负责撤销用户的登录的状态
     *
     * @return int
     */
    private function synlogout() {
        if (! self::API_SYNLOGOUT) return self::API_RETURN_FORBIDDEN;

        header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
        $this->load->library('session');
        $this->session->sess_destroy();
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 更改用户密码
     * 当用户更改用户密码时,此接口负责接受 UCenter 发来的新密码
     *
     * @return int
     */
    private function updatepw() {
        if (! self::API_UPDATEPW) return self::API_RETURN_FORBIDDEN;

        $username = $this->get['username'];
        $password = $this->get['password'];
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 更新关键字列表
     * UCenter_server 后台[词语过滤]变更时触发
     *
     * @return int
     */
    private function updatebadwords() {
        if (! self::API_UPDATEBADWORDS) return self::API_RETURN_FORBIDDEN;

        $cachefile = FCPATH . 'uc_client/data/cache/badwords.php';
        @unlink($cachefile);
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 更新域名解析缓存
     * UCenter_server 后台[域名解析]变更时触发
     *
     * @return int
     */
    private function updatehosts() {
        if (! self::API_UPDATEHOSTS) return self::API_RETURN_FORBIDDEN;

        $cachefile = FCPATH . 'uc_client/data/cache/hosts.php';
        $str = "<?php\r\n";
        $str .= '$_CACHE[\'hosts\'] = ' . var_export($this->post, true) . ";\r\n";
        file_put_contents($cachefile, $str);
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 更新应用列表
     * UCenter_server 后台[应用管理]变更时触发
     *
     * @return int
     */
    private function updateapps() {
        if (! self::API_UPDATEAPPS) return self::API_RETURN_FORBIDDEN;

        $cachefile = FCPATH . 'uc_client/data/cache/apps.php';
        $str = "<?php\r\n";
        $str .= '$_CACHE[\'apps\'] = ' . var_export($this->post, true) . ";\r\n";
        file_put_contents($cachefile, $str);
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 更新客户端缓存
     * UCenter_server 后台[基本设置][邮件设置]变更时触发
     *
     * @return int
     */
    private function updateclient() {
        if (! self::API_UPDATECLIENT) return self::API_RETURN_FORBIDDEN;

        $cachefile = FCPATH . 'uc_client/data/cache/settings.php';
        $str = "<?php\r\n";
        $str .= '$_CACHE[\'settings\'] = ' . var_export($this->post, true) . ";\r\n";
        file_put_contents($cachefile, $str);
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 更新用户积分
     * 当某应用执行了积分兑换请求的接口函数 uc_credit_exchange_request() 后
     * 此接口负责通知被兑换的目的应用程序所需修改的用户积分值
     *
     * @return int
     */
    private function updatecredit() {
        if (! self::API_UPDATECREDIT) return self::API_RETURN_FORBIDDEN;

        $credit = $this->get['credit'];
        $amount = $this->get['amount'];
        $uid = $this->get['uid'];
        return self::API_RETURN_SUCCEED;
    }

    /**
     * 向 UCenter 提供积分设置
     * 此接口负责把应用程序的积分设置传递给 UCenter, 以供 UCenter 在积分兑换设置中使用
     *
     * @return int
     */
    private function getcreditsettings() {
        if (! self::API_GETCREDITSETTINGS) return self::API_RETURN_FORBIDDEN;

        return self::API_RETURN_SUCCEED;
    }

    /**
     * 获取用户的某项积分
     * 此接口用于把应用程序中指定用户的积分传递给 UCenter
     *
     * @return int
     */
    private function getcredit() {
        if (! self::API_GETCREDIT) return self::API_RETURN_FORBIDDEN;

        $credits = array();
        return self::serialize($credits);
    }

    /**
     * 更新应用积分设置
     * 此接口负责接收 UCenter 积分兑换设置的参数
     *
     * @return int
     */
    private function API_UPDATECREDITSETTINGS() {
        if (! self::API_UPDATECREDITSETTINGS) return self::API_RETURN_FORBIDDEN;

        return self::API_RETURN_SUCCEED;
    }

    /************************ 第4部分 函数定义 ************************/

    /**
     * 加密解密
     * 通知类不需要用到uc_client多余的函数, 所以直接把加解密函数的内容copy过来
     * 而不是引入 uc_client/client.php, 从而减少IO的读取和内存的消耗
     *
     * @param string $string 内容
     * @param string $operation 类型(DECODE: 解密 ENCODE: 加密)
     * @param string $key 密钥
     * @param int $expiry 到期时间
     * @return array|string
     */
    public static function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) {
        $ckey_length = 4;
        $key = md5($key ? $key : UC_KEY);
        $keya = md5(substr($key, 0, 16));
        $keyb = md5(substr($key, 16, 16));
        $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length) : substr(md5(microtime()), -$ckey_length)) :
            '';

        $cryptkey = $keya . md5($keya . $keyc);
        $key_length = strlen($cryptkey);

        $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry +
            time() : 0) . substr(md5($string . $keyb), 0, 16) . $string;
        $string_length = strlen($string);

        $result = '';
        $box = range(0, 255);

        $rndkey = array();
        for ($i = 0; $i <= 255; $i++) {
            $rndkey[$i] = ord($cryptkey[$i % $key_length]);
        }

        for ($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndkey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }

        for ($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }

        if ($operation == 'DECODE') {
            if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr
                ($result, 26) . $keyb), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            return $keyc . str_replace('=', '', base64_encode($result));
        }
    }

    /**
     * XML序列化
     * 不引入 uc_client/client.php, 而是直接引入函数相关文件
     * 从而减少IO的读取和内存的消耗
     *
     * 注意:
     * xml_parse()需要解析的数据集中如果有元素的至为空会报错(Undefined index: xxxkey)
     * 所以函数加上@屏蔽错误显示
     */
    public static function serialize($arr, $htmlOn = 0) {
        if (! function_exists('xml_serialize')) {
            require FCPATH . 'uc_client/lib/xml.class.php';
        }
        return @xml_serialize($arr, $htmlOn);
    }

    /**
     * XML反序列化
     * 不引入 uc_client/client.php, 而是直接引入函数相关文件
     * 从而减少IO的读取和内存的消耗
     *
     * 注意:
     * xml_parse()需要解析的数据集中如果有元素的至为空会报错(Undefined index: xxxkey)
     * 所以函数加上@屏蔽错误显示
     */
    public static function unserialize($xml, $htmlOn = 0) {
        if (! function_exists('xml_serialize')) {
            require FCPATH . 'uc_client/lib/xml.class.php';
        }
        return @xml_unserialize($xml, $htmlOn);
    }
}

/* End of file Uc_note.php */
/* Location: ./application/libraries/uc/Uc_note.php */
